{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### yield特点\n",
    ">- 只能用在函数内\n",
    ">- 在函数内的任何地方出现yield关键字，哪怕永远无法被执行到，函数都会发生变异(执行该函数，不再直接执行)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<generator object gen at 0x000001DE1EC79A80>\n",
      "<class 'function'>\n",
      "<class 'generator'>\n"
     ]
    }
   ],
   "source": [
    "def gen():\n",
    "    print(\"hello\")\n",
    "    if False:\n",
    "        yield\n",
    "\n",
    "g = gen()\n",
    "print(g)\n",
    "print(type(gen))\n",
    "print(type(g))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "True\n",
      "True\n",
      "True\n",
      "False\n"
     ]
    }
   ],
   "source": [
    "import inspect\n",
    "\n",
    "# 是函数，也是生成器函数\n",
    "print(inspect.isfunction(gen))\n",
    "print(inspect.isgeneratorfunction(gen))\n",
    "\n",
    "# 生成器函数不是生成器\n",
    "print(inspect.isgenerator(g))\n",
    "print(inspect.isgenerator(gen))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 生成器对象是迭代器\n",
    "\n",
    "g = gen()\n",
    "hasattr(g, '__iter__') and hasattr(g, '__next__')\n",
    "\n",
    "g is iter(g) # 迭代器 要求 __iter__ 返回自身"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### yield的作用\n",
    ">- 调用生成器函数不是直接执行其中的代码，而是返回一个对象；\n",
    ">- 生成器函数内的代码，需要通过生成器对象来执行。\n",
    "\n",
    "在作用上，类似于 class。\n",
    "\n",
    "生成器对象就是迭代器，所以它的运行方式和迭代器是一致的。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "def gen_42(meet_yield):\n",
    "    print(\"Hola\")\n",
    "    if meet_yield:\n",
    "        print(\"yield\")\n",
    "        yield 42\n",
    "        print(\"yielded 42\")\n",
    "    print(\"bye\")\n",
    "    return \"result\"\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hola\n",
      "bye\n"
     ]
    },
    {
     "ename": "StopIteration",
     "evalue": "result",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mStopIteration\u001b[0m                             Traceback (most recent call last)",
      "Cell \u001b[1;32mIn[5], line 2\u001b[0m\n\u001b[0;32m      1\u001b[0m g1 \u001b[38;5;241m=\u001b[39m gen_42(\u001b[38;5;28;01mFalse\u001b[39;00m)\n\u001b[1;32m----> 2\u001b[0m x1 \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mnext\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mg1\u001b[49m\u001b[43m)\u001b[49m \u001b[38;5;66;03m# return 语句触发 StopIteration\u001b[39;00m\n\u001b[0;32m      3\u001b[0m \u001b[38;5;28mprint\u001b[39m(x1)\n",
      "\u001b[1;31mStopIteration\u001b[0m: result"
     ]
    }
   ],
   "source": [
    "g1 = gen_42(False)\n",
    "x1 = next(g1) # return 语句触发 StopIteration\n",
    "print(x1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hola\n",
      "yield\n",
      "42\n"
     ]
    }
   ],
   "source": [
    "g2 = gen_42(True)\n",
    "x2 = next(g2)\n",
    "print(x2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "yielded 42\n",
      "bye\n"
     ]
    },
    {
     "ename": "StopIteration",
     "evalue": "result",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mStopIteration\u001b[0m                             Traceback (most recent call last)",
      "Cell \u001b[1;32mIn[7], line 1\u001b[0m\n\u001b[1;32m----> 1\u001b[0m \u001b[38;5;28;43mnext\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mg2\u001b[49m\u001b[43m)\u001b[49m \u001b[38;5;66;03m# 从上一次 yield 继续执行，直到遇到下一个 yield 或 return\u001b[39;00m\n",
      "\u001b[1;31mStopIteration\u001b[0m: result"
     ]
    }
   ],
   "source": [
    "next(g2) # 从上一次 yield 继续执行，直到遇到下一个 yield 或 return"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "def count(start = 0, step = 1):\n",
    "    n = start\n",
    "    while True:\n",
    "        yield n\n",
    "        n += step"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 生成器的 4 个状态\n",
    ">- 当调用生成器函数得到生成器对象时\n",
    ">>- 此时的生成器对象可以理解为处于初始状态\n",
    ">- 通过 next() 调用生成器对象，对应的生成器函数代码开始运行\n",
    ">>- 此时生成器对象处于运行中状态\n",
    ">- 如果遇到了 yield 语句，next() 返回时\n",
    ">>- yield 语句右边的对象作为 next() 的返回值\n",
    ">>- 生成器在 yield 语句所在的位置暂停，当再次使用 next() 时继续从该位置继续运行\n",
    ">- 如果执行到函数结束，则抛出 StopIteration 异常\n",
    ">>- 不管是使用了 return 语句显式地返回值，或者默认返回 None 值，返回值都只能作为异常的值一并抛出\n",
    ">>- 此时的生成器对象处于结束的状态\n",
    ">>- 对于已经结束的生成器对象再次调用 next()，直接抛出 StopIteration 异常，并且不含返回值"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 应用场景"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    ">- 定义一个容器类的可迭代对象，为该对象实现__iter__接口"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 场景一\n",
    "# 迭代器类\n",
    "class MyCustomDataIterator:\n",
    "    def __init__(self, data):\n",
    "        self.data = data\n",
    "        self.index = -1\n",
    "    \n",
    "    def __iter__(self):\n",
    "        return self\n",
    "    \n",
    "    def __next__(self):\n",
    "        self.index += 1\n",
    "        if self.index < self.data.size:\n",
    "            return self.data[self.index]\n",
    "        else:\n",
    "            return StopIteration\n",
    "\n",
    "# 可迭代数据类\n",
    "class MyCustomData:\n",
    "    @property\n",
    "    def size(self):\n",
    "        return self.size\n",
    "    \n",
    "    def get_value(self, index):\n",
    "        return index\n",
    "    \n",
    "    def __iter__(self):\n",
    "        return MyCustomDataIterator(self)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 可迭代数据类\n",
    "class MyCustomData:\n",
    "    @property\n",
    "    def size(self):\n",
    "        return self.size\n",
    "    \n",
    "    def get_value(self, index):\n",
    "        return index\n",
    "    \n",
    "    def __iter__(self):\n",
    "        index = -1\n",
    "        while index < 2:\n",
    "            index += 1\n",
    "            yield self.get_value(index)\n",
    "            return self.data[self.index]\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### 定义一个处理其他可迭代对象的迭代器"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "BLACK_LIST = [\"kill\", \"murder\"]\n",
    "\n",
    "class SuzhiIterator:\n",
    "    def __init__(self, actions):\n",
    "        self.actions = actions\n",
    "        self.index = 0\n",
    "    \n",
    "    def __iter__(self):\n",
    "        return self\n",
    "    \n",
    "    def __next__(self):\n",
    "        while self.index < len(self.actions):\n",
    "            self.index += 1\n",
    "            action = self.actions[self.index]\n",
    "            if action in BLACK_LIST:\n",
    "                continue\n",
    "            elif action == \"sleep\":\n",
    "                return \"sleeping\"\n",
    "            else:\n",
    "                return action\n",
    "        raise StopIteration"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "exercise\n",
      "sleeping\n"
     ]
    }
   ],
   "source": [
    "BLACK_LIST = [\"kill\", \"murder\"]\n",
    "\n",
    "def Suzhi(actions):\n",
    "    for action in actions:\n",
    "        if action in BLACK_LIST:\n",
    "            continue\n",
    "        elif action == \"sleep\":\n",
    "            yield \"sleeping\"\n",
    "        else:\n",
    "            yield action\n",
    "\n",
    "actions = [\"exercise\", \"murder\", \"sleep\"]\n",
    "for x in Suzhi(actions):\n",
    "    print(x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    ">- 定义一个不依赖数据存储的数据生成器"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "class DownCount:\n",
    "    def __init__(self, start):\n",
    "        self.start = start\n",
    "    \n",
    "    def __iter__(self):\n",
    "        return self\n",
    "    \n",
    "    def __next__(self):\n",
    "        if self.start < 0:\n",
    "            raise StopIteration\n",
    "        else:\n",
    "            current = self.start\n",
    "            self.start -= 1\n",
    "            return current"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "3\n",
      "2\n",
      "1\n",
      "0\n"
     ]
    }
   ],
   "source": [
    "def DownCount(start):\n",
    "    while start >= 0:\n",
    "        yield start\n",
    "        start -= 1\n",
    "\n",
    "for x in DownCount(3):\n",
    "    print(x)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "base",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
